
/**
 * This file is part of iSecurePasswords.
 *
 *   iSecurePasswords is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   iSecurePasswords is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with iSecurePasswords.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author: Matthew MacGregor
 * Created: 2012
 * iSecurePasswords, copyright 2013 Sudolink, LLC
 * 
 */ package com.sudolink.cryptodata;

import com.matsuzine.utils.HelperMethods;
import com.sudolink.crypto.AESCrypto;
import com.sudolink.crypto.CryptoException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author matsu
 */
public class SecureData implements ISecureData {

    private Map<String, String> node;
    private String name;
    //Values may be encrypted for safety, but there are cases where they will
    //not be (such as after exporting the values to JSON, but before encrypting
    //them again. NOTE: Needs to be true by default, otherwise when objects are
    //deserialized get() will not decrypt them appropriately.
    private boolean isEncrypted = true;

    public SecureData() {
        node = new HashMap<>();
        name = "";
    }

    @Override
    public String get(String key, CryptInfoBundle bundle) {
        String value;

        if (bundle == null) {
            Logger.getLogger(SecureData.class.getName()).log(Level.SEVERE, null, "Bundle cannot be null");
            return "";
        }
        
        String temp = node.get(key);
        if ( temp == null ) {
            return "";
        }
        
        //If the value has been decrypted in place, isEncrypted will be false.
        //In all other situations, it will be true.
        if ( isEncrypted == true ) {
    
            AESCrypto c = new AESCrypto();

            try {
                byte[] v = c.decrypt(temp, bundle.getPassword(), bundle.getSalt());
                char[] t = HelperMethods.convertByteArrayToCharArray(v);
                value = new String(t);

            } catch (CryptoException ex) {
                Logger.getLogger(SecureData.class.getName()).log(Level.SEVERE, null, ex);
                value = temp;
            }
            
        } else {
            value = temp;
        }
        
        return value;
    }

    @Override
    public void set(String key, String value, CryptInfoBundle bundle) {
        set(key, value.toCharArray(), bundle);
    }

    @Override
    public void set(String key, char[] value, CryptInfoBundle bundle) {
        
        if (value.length == 0) {
            return;
        }
        
        String valueAfterEncryption = null;
        if (bundle == null) {
            Logger.getLogger(SecureData.class.getName()).log(Level.SEVERE, null, "Bundle cannot be null");
            return;
        }

        byte[] bytes = com.matsuzine.utils.HelperMethods.convertCharArrayToByteArray(value);
        AESCrypto c = new AESCrypto();
        try {
            valueAfterEncryption = c.encrypt(bytes, bundle.getPassword(), bundle.getSalt());
        } catch (CryptoException ex) {
            Logger.getLogger(SecureData.class.getName()).log(Level.SEVERE, null, ex);
            //Encryption has failed for no good. We may want to throw an exception
        }

        if (valueAfterEncryption != null) {
            node.put(key, valueAfterEncryption);
        }

        
        
    }

    /**
     * Decrypts all values in place. Use with caution!
     *
     * @param bundle
     */
    public void decrypt(CryptInfoBundle bundle) {
        Set<String> keys = node.keySet();

        for (String k : keys) {
            String val = get(k, bundle);
            node.put(k, val);
        }
        
        isEncrypted = false;
    }

    /**
     * Encrypts all values in place. Use with caution!
     *
     * @param bundle
     */
    public void encrypt(CryptInfoBundle bundle) {
        Set<String> keys = node.keySet();

        for (String k : keys) {
            String val = get(k, bundle);
            set(k, val, bundle); //Reset the value 
        }
        
        isEncrypted = true;
    }

    @Override
    public String toString() {
        return getName();
    }

    /**
     * @return the name
     */
    @Override
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    @Override
    public void setName(String name) {
        this.name = name;
    }
}
